# coding=utf-8
import sys

reload(sys)
sys.setdefaultencoding('utf8')
import utils
import socket
import json

logger = utils.get_logger(__name__)


class GoBangAI(object):
    def __init__(self):
        """
        初始化AI相关参数
        """
        logger.info(u'正在初始化AI程序...')
        self.self_agent = 0
        self.other_agent = 0
        self.search_deep = 12
        self.dx = [0, 1, 0, -1, -1, -1, 1, 1, -2, -1, 0, 1, 2, -2, -1, 0, 1, 2, -2, -2, -2, -2, -2, 2, 2, 2, 2, 2]
        self.dy = [1, 0, -1, 0, -1, 1, -1, 1, -2, -2, -2, -2, -2, 2, 2, 2, 2, 2, -2, -1, 0, 1, 2, -2, -1, 0, 1, 2]
        self.cheesboard_size = utils.CHESSBOARD_SIZE  # 棋盘尺寸，这里采用标准的15*15
        self.init_cheesboard()
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.deal_with_server()

    def init_cheesboard(self):
        """
        初始化棋盘为全0数组
        :return:
        """
        self.cheesboard = [[0 for i in range(self.cheesboard_size)] for j in range(self.cheesboard_size)]  # 初始化一个全为0的棋盘

    def deal_with_server(self):
        """
        与服务器进行连接
        :return:
        """
        logger.info(u'正在连接到服务器...')
        self.client.connect(('127.0.0.1', 9887))
        logger.info(u'成功连接到服务器，正在验证身份...')
        self.client.send('ui-client')
        self.self_agent = 1
        self.other_agent = 2

    def compute_next_step(self):
        """
        计算下一步落子方位
        :return:
        """
        cnt = 0
        # 检查当前是否是第一次落子
        for i in self.cheesboard:
            for j in i:
                if j != 0:
                    cnt += 1
                    if cnt > 1:
                        break
        if cnt == 0:  # 第一次落子
            return self.cheesboard_size / 2, self.cheesboard_size / 2
        elif cnt == 1:  # 第二次落子
            mid_pos = self.cheesboard_size / 2
            if self.cheesboard[mid_pos][mid_pos] == 0:
                return mid_pos, mid_pos
            else:
                dx = [0, 1, 0, -1]
                dy = [1, 0, -1, 0]
                for i in range(4):
                    nx = mid_pos + dx[i]
                    ny = mid_pos + dy[i]
                    if self.cheesboard[nx][ny] == 0:
                        return nx, ny
        else:  # 其他
            return self.alpha_beta_search(deep=0, is_max=True, par_alpha_beta_value=0, par_has_alpha_beta_value=False)

    def alpha_beta_search(self, deep, is_max, par_alpha_beta_value, par_has_alpha_beta_value):
        """
        alpha-beta剪枝搜索，获取落子方位和局势估分
        :param deep:当前搜索深度
        :param is_max:当前搜索是否取极大值
        :return:
        """
        if deep == self.search_deep:
            return self.compute_score()
        else:
            alpha_beta_value = 0  # 记录该节点的alpha值或beta值
            has_alpha_beta_value = False  # 记录该节点是否存在alpha值或beta值
            x_pos = 0  # 最佳落子的x坐标
            y_pos = 0  # 最佳落子的y坐标
            for i in range(self.cheesboard_size):
                for j in range(self.cheesboard_size):
                    if self.check_need_compute(i, j):  # 检查当前节点是否需要进行搜索
                        self.cheesboard[i][j] = self.self_agent  # 标记
                        tmp_value = self.alpha_beta_search(deep=deep + 1, is_max=(not is_max),
                                                           par_alpha_beta_value=alpha_beta_value,
                                                           par_has_alpha_beta_value=has_alpha_beta_value)
                        self.cheesboard[i][j] = 0  # 回溯
                        if not has_alpha_beta_value:  # 若当前节点尚不存在alpha或beta值，直接进行覆盖
                            alpha_beta_value = tmp_value
                            has_alpha_beta_value = True
                            x_pos = i
                            y_pos = j
                        else:  # 若已存在alpha或beta值
                            if is_max:  # 若当前为max层，则取极大值
                                if tmp_value > alpha_beta_value:
                                    x_pos = i
                                    y_pos = j
                                    alpha_beta_value = tmp_value
                            else:  # 否则，取极小值
                                if tmp_value < alpha_beta_value:
                                    x_pos = i
                                    y_pos = j
                                    alpha_beta_value = tmp_value
                        if par_has_alpha_beta_value:  # 当父节点存在alpha-beta值时，需要检查是否进行剪枝
                            if is_max:
                                if alpha_beta_value >= par_alpha_beta_value:  # beta剪枝
                                    return alpha_beta_value
                            else:
                                if alpha_beta_value <= par_alpha_beta_value:  # alpha剪枝
                                    return alpha_beta_value
            if deep != 0:  # 根节点
                return alpha_beta_value
            else:  # 非根节点：
                return x_pos, y_pos

    def check_need_compute(self, x, y):
        """
        检查当前位置是否需要进行搜索
        这里认为，当一个点的方圆2内都不存在其他点时，这个点不需要进行搜索
        :param x:当前点的x值
        :param y:当前点的y值
        :return:需要计算，True，不需要计算，False
        """
        if self.cheesboard[x][y] != 0:  # 如果棋盘上该位置已存在棋子，则不需要搜索
            return False
        d_lenth = len(self.dx)
        for i in range(d_lenth):
            nx = x + self.dx[i]
            ny = y + self.dy[i]
            if nx >= 0 and ny >= 0 and nx < self.cheesboard_size and ny < self.cheesboard_size:
                if self.cheesboard[nx][ny] != 0:
                    return True
        return False

    def check_left_and_right(self,row,col,l_num,r_num,other_agent):
        """
        检查左右两边是否可落子
        :param row: 当前光标行号
        :param col: 当前光标列好
        :param l_num: 左边减数
        :param r_num: 右边加数
        :param other_agent: 对手标识
        :return: 0-两边均不能落子 1-其中一边可以落子 2-两边均可落子
        """
        left_ok = 1
        right_ok = 1
        if col + r_num >= self.cheesboard_size or self.cheesboard[row][col] == other_agent:
            right_ok = 0
        if col - l_num < 0 or self.cheesboard[row][col - l_num] == other_agent:
            left_ok = 0
        is_ok = left_ok + right_ok
        return is_ok

    def count_socre(self, self_agent, other_agent):
        """
        统计当前局势上，主体的得分
        :param self_agent: 棋盘主体
        :param other_agent: 棋盘对手
        :return:计算出来的最终分数
        """
        FIVE = 0  # 成五
        FOUR = 1  # 活四
        SFOUR = 2  # 冲四
        THREE = 3  # 活三
        STHREE = 4  # 冲三
        TWO = 5  # 活二
        STWO = 6  # 冲二
        ONE = 7  # 单子
        self.score_dict = {}
        self.score_dict[self_agent] = [0 for i in range(8)]
        self.score_dict[other_agent] = [0 for i in range(8)]

        # 横向统计
        for row in range(self.cheesboard_size):
            col = 0
            space_cnt = 0
            self_cnt = 0
            # 对于每一行
            while col < self.cheesboard_size:
                if self.cheesboard[row][col] == 0:  # 空位，跳过
                    col += 1
                    continue
                # 检查每一列
                if self.cheesboard[row][col] == self_agent:
                    # 统计连续的棋子数
                    while col < self.cheesboard_size and self.cheesboard[row][col] == self_agent:
                        self_cnt += 1
                        col += 1
                    # 连续五子
                    if self_cnt >= 5:
                        self.score_dict[self_agent][FIVE] += 1
                    # 连续四子
                    elif self_cnt == 4:
                        # 检查两端，是否活4
                        is_ok = self.check_left_and_right(row,col,5,0,other_agent)
                        # 两端堵死，无用棋
                        if is_ok == 0:
                            self.score_dict[self_agent][ONE] += 1
                        # 一端堵死，冲四
                        elif is_ok == 1:
                            self.score_dict[self_agent][SFOUR] += 1
                        # 活四
                        else:
                            self.score_dict[self_agent][FOUR] += 1
                    # 连续3子
                    elif self_cnt == 3:
                        # 检查是否能形成跳四
                        if col + 1 < self.cheesboard_size and self.cheesboard[row][col] == 0 and self.cheesboard[row][
                                    col + 1] == self_agent:  # 检查右边是否还能落2子，并且形成跳四
                            self.score_dict[self_agent][SFOUR] += 1
                            # 列标记后移两格
                            col += 2
                            continue
                        # 检查左右两端
                        is_ok = self.check_left_and_right(row,col,4,0,other_agent)
                        if is_ok == 0:  # 两端堵死
                            self.score_dict[self_agent][ONE] += 1
                        elif is_ok == 1:  # 一端堵死
                            self.score_dict[self_agent][STHREE] += 1
                        else:  # 活三
                            self.score_dict[self_agent][THREE] += 1
                    # 连续两子
                    elif self_cnt == 2:
                        # 检查是否形成跳四
                        if col + 2 < self.cheesboard_size and self.cheesboard[row][col] == 0 and self.cheesboard[row][
                                    col + 1] == self_agent and self.cheesboard[row][col + 2] == self_agent:
                            self.score_dict[self_agent][SFOUR] += 1
                            col += 3
                            continue
                        # 检查是否形成跳三
                        if col + 1 < self.cheesboard_size and self.cheesboard[row][col] == 0 and self.cheesboard[row][
                                    col + 1] == self_agent:
                            is_ok = self.check_left_and_right(row,col,3,2,other_agent)
                            if is_ok == 0:
                                self.score_dict[self_agent][ONE] += 1
                            elif is_ok == 1:
                                self.score_dict[self_agent][STHREE] += 1
                            elif is_ok == 2:
                                self.score_dict[self_agent][THREE] += 1
                            col += 2
                            continue
                        # 检查是否活二
                        is_ok=self.check_left_and_right(row,col,3,0,other_agent)
                        if is_ok==0:
                            self.score_dict[self_agent][ONE]+=1
                        elif is_ok==1:
                            self.score_dict[self_agent][STWO]+=1
                        else:
                            self.score_dict[self_agent][TWO]+=1
                    elif self_cnt == 1:
                        # 检查是否形成跳四
                        if col + 3 < self.cheesboard_size and self.cheesboard[row][col] == 0 and self.cheesboard[row][
                                    col + 1] == self_agent and self.cheesboard[row][col + 2] == self_agent and \
                                        self.cheesboard[row][col + 3] == self_agent:
                            self.score_dict[self_agent][SFOUR] += 1
                            col += 4
                            continue
                        # 检查是否形成跳三
                        if col + 2 < self.cheesboard_size and self.cheesboard[row][col] == 0 and self.cheesboard[row][
                                    col + 1] == self_agent and self.cheesboard[row][col + 2] == self_agent:
                            is_ok=self.check_left_and_right(row,col,2,3,other_agent)
                            if is_ok==0:
                                self.score_dict[self_agent][ONE]+=1
                            elif is_ok==1:
                                self.score_dict[self_agent][STHREE]+=1
                            else:
                                self.score_dict[self_agent][THREE]+=1
                            col+=3
                            continue
                        self.score_dict[self_agent][ONE]+=1


    def compute_score(self):
        """
        估计局势得分
        :return:
        """
        return 0


if __name__ == '__main__':
    GoBangAI()
